/*
 * ST7735.c
 *
 *  Created on: Nov 8, 2012
 *      Author: bel671
 *
 *
 * https://github.com/siy/openstm32library/blob/master/human-interface/ST7735/wirish/ST7735.cpp
 */

#include <stdlib.h>
#include "ST7735.h"

#define _cs 3
#define _rs 5
#define ST_IOPORT GPIOA
#define RESET_PIN 4

#define int16 int16_t
#define uint16 uint16_t
#define uint32 uint32_t
#define uint8 uint8_t

#define LOW 0
#define HIGH 1

#define LCD_SPI &SPID1


int width = 100;
int height = 100;

int madctl;

#define swap(a, b) { uint16 t = a; a = b; b = t; }

//volatile AT91PS_SPI pSPI = AT91C_BASE_SPI0;


//static __inline void GDISP_LLD(write_spi)(uint16_t data) {
//	// wait for the previous transfer to complete
//	while((pSPI->SPI_SR & AT91C_SPI_TXEMPTY) == 0);
//	// send the data
//	pSPI->SPI_TDR = data;
//}

static uint8_t txbuf[20];

void spiwrite(uint8 command) {
	spiSelect(LCD_SPI);
	txbuf[0] = command;
	spiSend(LCD_SPI, 1, txbuf);
	spiUnselect(LCD_SPI);
}

void spiwrite2(uint32 size, uint8* buf) {
	spiSelect(LCD_SPI);
	spiSend(LCD_SPI, size, buf);
	spiUnselect(LCD_SPI);
}

static __inline void digitalWrite(int pin, int booleanValue) {
	if (booleanValue)
		turnPinOff(ST_IOPORT, pin);
	else
		turnPinOn(ST_IOPORT, pin);
}

void writecommand(uint8 c) {
	digitalWrite(_rs, LOW);
	spiwrite(c);
}

void writeFullCommand(uint8 c) {
	digitalWrite(_cs, LOW);
	writecommand(c);
	digitalWrite(_cs, HIGH);
}

void writecommandAndParms(uint8 c, uint32 numparms, uint8* params) {
	writecommand(c);
	digitalWrite(_rs, HIGH);
	spiwrite2(numparms, params);
}

void writecommand1(uint8 c, uint8 d0) {
	txbuf[0] = d0;
	writecommandAndParms(c, 1, txbuf);
}

void writecommand2(uint8 c, uint8 d0, uint8 d1) {
	txbuf[0] = d0;
	txbuf[1] = d1;
	writecommandAndParms(c, 2, txbuf);
}

void writecommand3(uint8 c, uint8 d0, uint8 d1, uint8 d2) {
	txbuf[0] = d0;
	txbuf[1] = d1;
	txbuf[2] = d2;
	writecommandAndParms(c, 3, txbuf);
}

void setRotation(uint8 m) {
	digitalWrite(_cs, LOW);
	writecommand1(ST7735_MADCTL, madctl = m);
	digitalWrite(_cs, HIGH);
}

void setAddrWindow(uint8 x0, uint8 y0, uint8 x1, uint8 y1) {
	uint8 parmCaset[4] = { 0x00, x0, 0x00, x1 };
	uint8 parmRaset[4] = { 0x00, y0, 0x00, y1 };
	writecommandAndParms(ST7735_CASET, sizeof(parmCaset), parmCaset); //START/END
	writecommandAndParms(ST7735_RASET, sizeof(parmRaset), parmRaset); //START/END

	writecommand(ST7735_RAMWR); // write to RAM
}

void pushColor(uint16 color, int count) {
	uint8 colorData[2] = { (color >> 8) & 0xFF, color & 0xFF };
	digitalWrite(_rs, HIGH);

	for (int i = 0; i < count; i++) {
		spiwrite2(sizeof(colorData), colorData);
	}
}

void drawPixelInt(uint16_t x, uint16_t y, uint16_t color);

void drawPixel(uint8 x, uint8 y, uint16 color) {
	if ((x >= width) || (y >= height))
		return;

	digitalWrite(_cs, LOW);
	drawPixelInt(x, y, color);
	digitalWrite(_cs, HIGH);
}

//void fillScreen(uint16 color) {
//	fillRect(0, 0, width, height, color);
//}

/*
 * SPI1 configuration structure.
 * Speed 5.25MHz, CPHA=1, CPOL=1, 8bits frames, MSb transmitted first.
 * The slave select line is the pin GPIOE_CS_SPI on the port GPIOE.
 */
static const SPIConfig spi1cfg = { NULL,
/* HW dependent part.*/GPIOE, GPIOE_CS_SPI, SPI_CR1_BR_0 | SPI_CR1_BR_1
| SPI_CR1_CPOL | SPI_CR1_CPHA };



void init(uint8 mode) {
// set pin directions
//todo	pinMode(_rs, OUTPUT);

//todo	_spi.begin(SPI_18MHZ, MSBFIRST, SPI_MODE_3);

// toggle RST low to reset; CS low so it'll listen to us
	//todo	pinMode(_cs, OUTPUT);
	digitalWrite(_cs, LOW);
	if (RESET_PIN) {
		//todo		pinMode(_rst, OUTPUT);
		digitalWrite(RESET_PIN, HIGH);
		chThdSleepMilliseconds(500);
		digitalWrite(RESET_PIN, LOW);
		chThdSleepMilliseconds(500);
		digitalWrite(RESET_PIN, HIGH);
		chThdSleepMilliseconds(500);
	}
	 writecommand(ST7735_SWRESET); // software reset
	 chThdSleepMilliseconds(150);

	 writecommand(ST7735_SLPOUT);// out of sleep mode
	 chThdSleepMilliseconds(500);

	 // frame rate = fosc / (1 x 2 + 40) * (LINE + 2C + 2D)
	 // same rate setting for all modes, normal, idle, partial
	 writecommand3(ST7735_FRMCTR1, 0x01, 0x2C, 0x2D);// frame rate control - normal mode
	 writecommand3(ST7735_FRMCTR2, 0x01, 0x2C, 0x2D);// frame rate control - idle mode
	 uint8 rateParm3[6] = {0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D}; // dot inversion mode, line inversion mode
	 writecommandAndParms(ST7735_FRMCTR3, sizeof(rateParm3), rateParm3);// frame rate control - partial mode

	 writecommand1(ST7735_INVCTR, 0x07);// display inversion control, no inversion

	 writecommand3(ST7735_PWCTR1, 0xA2, 0x02, 0x84);// power control, -4.6V, AUTO mode
	 writecommand1(ST7735_PWCTR2, 0xC5);// power control, VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD
	 writecommand2(ST7735_PWCTR3, 0x0A, 0x00);// power control, Opamp current small, Boost frequency
	 writecommand2(ST7735_PWCTR4, 0x8A, 0x2A);// power control, BCLK/2, Opamp current small & Medium low
	 writecommand2(ST7735_PWCTR5, 0x8A, 0xEE);// power control
	 writecommand1(ST7735_VMCTR1, 0x0E);// power control

	 writecommand(ST7735_INVOFF);// don't invert display

	 madctl = (mode == ST7735_PORTRAIT) ? 0x08 : 0x68;
	 writecommand1(ST7735_MADCTL, madctl);// memory access control (directions)
	 writecommand1(ST7735_COLMOD, 0x05);// set color mode, 16-bit color

	 width = (mode == ST7735_PORTRAIT) ? 128 : 160;
	 height = (mode == ST7735_PORTRAIT) ? 160 : 128;

	 uint8 addrSetParms[] = {0x00, 0x00, 0x00, width - 1};
	 writecommandAndParms(ST7735_CASET, sizeof(addrSetParms), addrSetParms); // column addr set
	 addrSetParms[3] = height - 1;
	 writecommandAndParms(ST7735_RASET, sizeof(addrSetParms), addrSetParms);// column addr set

	 //Gamma sets
	 uint8 gammaSet1[] = {0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d,
	 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10};
	 uint8 gammaSet2[] = {0x03, 0x1d, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D,
	 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10};

	 writecommandAndParms(ST7735_GMCTRP1, sizeof(gammaSet1), gammaSet1);
	 writecommandAndParms(ST7735_GMCTRN1, sizeof(gammaSet2), gammaSet2);

	 writecommand(ST7735_DISPON);
	 chThdSleepMilliseconds(100);

	 writecommand(ST7735_NORON); // normal display on
	 chThdSleepMilliseconds(10);
	 digitalWrite(_cs, HIGH);

}

void initSt7735() {


	spiStart(LCD_SPI, &spi1cfg);


	init(ST7735_PORTRAIT);


	for(int i =0;i<100;i++)
		drawPixelInt(i, i, LCD_COLOR_RED);

}


//// draw a string from memory
//
//void drawString(uint8 x, uint8 y, const char *c, uint16 color, uint8 size) {
//	while (c[0] != 0) {
//		drawChar(x, y, c[0], color, size);
//		x += size * 6;
//		c++;
//		if (x + 5 >= width) {
//			y += 10;
//			x = 0;
//		}
//	}
//}

void drawPixelInt(uint16_t x, uint16_t y, uint16_t color);
void drawVerticalLine(uint8 x, uint8 y, uint8 length, uint16 color);

// draw a rectangle
//void drawRect(uint8 x, uint8 y, uint8 w, uint8 h, uint16 color) {
// smarter version
//	drawHorizontalLine(x, y, w, color);
//	drawHorizontalLine(x, y + h - 1, w, color);
//	drawVerticalLine(x, y, h, color);
//	drawVerticalLine(x + w - 1, y, h, color);
//}

//void fillRect(uint8 x, uint8 y, uint8 w, uint8 h, uint16 color) {
//// smarter version
//	digitalWrite(_cs, LOW);
//	fillRectInt(x, y, w, h, color);
//	digitalWrite(_cs, HIGH);
//}


void drawFastLine(uint8 x, uint8 y, uint8 length, uint16 color, uint8 rotflag);

void drawVerticalLine(uint8 x, uint8 y, uint8 length, uint16 color) {
	if (x >= width)
		return;
	if (y + length >= height)
		length = height - y - 1;

	drawFastLine(x, y, length, color, 1);
}

//void drawPixelInt(uint16_t x, uint16_t y, uint16_t color) {
//todo
//	if (y >= height)
//		return;
//	if (x + length >= width)
//		length = width - x - 1;

//	drawFastLine(x, y, length, color, 0);
//}

void drawFastLine(uint8 x, uint8 y, uint8 length, uint16 color, uint8 rotflag) {
	digitalWrite(_cs, LOW);
	if (rotflag) {
		setAddrWindow(x, y, x, y + length);
	} else {
		setAddrWindow(x, y, x + length, y + 1);
	}

	pushColor(color, length);
	digitalWrite(_cs, HIGH);
}

//int16 abs(int16 value) {
//	return value < 0 ? -value : value;
//}


// bresenham's algorithm - thx wikpedia
void drawLine(int16 x0, int16 y0, int16 x1, int16 y1, uint16 color) {
	uint16 steep = abs(y1 - y0) > abs(x1 - x0);
	if (steep) {
		swap(x0, y0);
		swap(x1, y1);
	}

	if (x0 > x1) {
		swap(x0, x1);
		swap(y0, y1);
	}

	uint16 dx, dy;
	dx = x1 - x0;
	dy = abs(y1 - y0);

	int16 err = dx / 2;
	int16 ystep;

	if (y0 < y1) {
		ystep = 1;
	} else {
		ystep = -1;
	}
	digitalWrite(_cs, LOW);

	for (; x0 <= x1; x0++) {
		if (steep) {
			drawPixelInt(y0, x0, color);
		} else {
			drawPixelInt(x0, y0, color);
		}
		err -= dy;
		if (err < 0) {
			y0 += ystep;
			err += dx;
		}
	}
	digitalWrite(_cs, HIGH);
}

void writedata(uint8_t p) {
	spiwrite(p);
}

void drawPixelInt(uint16_t x, uint16_t y, uint16_t color) {
	//setAddrWindow(x, y, x + 1, y + 1);
	setAddrWindow(0, 0, 100 + 1, 100 + 1);
	writecommand(ST7735_RAMWR);  // write to RAM
	writedata(color >> 8);
	writedata(color);
}

//void setAddrWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1)
//{
//  writecommand(ST7735_CASET);  // column addr set
//  writedata(0x00);
//  writedata(x0+1);   // XSTART
//  writedata(0x00);
//  writedata(x1+1);   // XEND
//
//  writecommand(ST7735_RASET);  // row addr set
//  writedata(0x00);
//  writedata(y0+2);    // YSTART
//  writedata(0x00);
//  writedata(y1+2);    // YEND
//
//  writecommand(ST7735_RAMWR);  // write to RAM
//}

